//===--- ParseIfConfig.cpp - Swift Language Parser for #if directives -----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Conditional Compilation Block Parsing and AST Building
//
//===----------------------------------------------------------------------===//

#include "polarphp/llparser/Parser.h"

#include "polarphp/ast/ASTVisitor.h"
#include "polarphp/ast/DiagnosticSuppression.h"
#include "polarphp/basic/Defer.h"
#include "polarphp/kernel/Version.h"
#include "polarphp/kernel/LangOptions.h"
#include "polarphp/llparser/Lexer.h"
//#include "polarphp/llparser/SyntaxParsingContext.h"
//#include "polarphp/syntax/SyntaxFactory.h"
//#include "polarphp/syntax/TokenSyntax.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"

namespace polar::llparser {

using polar::PlatformConditionKind;

namespace {

/// Get PlatformConditionKind from platform condition name.
static
Optional<PlatformConditionKind> getPlatformConditionKind(StringRef Name) {
   return llvm::StringSwitch<Optional<PlatformConditionKind>>(Name)
#define PLATFORM_CONDITION(LABEL, IDENTIFIER) \
    .Case(IDENTIFIER, PlatformConditionKind::LABEL)

#include "polarphp/ast/PlatformConditionKindsDef.h"

      .Default(None);
}

/// Get platform condition name from PlatformConditionKind.
static StringRef getPlatformConditionName(PlatformConditionKind Kind) {
   switch (Kind) {
#define PLATFORM_CONDITION(LABEL, IDENTIFIER) \
  case PlatformConditionKind::LABEL: return IDENTIFIER;

#include "polarphp/ast/PlatformConditionKindsDef.h"
   }
   llvm_unreachable("Unhandled PlatformConditionKind in switch");
}

/// Extract source text of the expression.
static StringRef extractExprSource(SourceManager &SM, Expr *E) {
   CharSourceRange Range =
      Lexer::getCharSourceRangeFromSourceRange(SM, E->getSourceRange());
   return SM.extractText(Range);
}

static bool isValidPrefixUnaryOperator(Optional<StringRef> UnaryOperator) {
   return UnaryOperator != None &&
          (UnaryOperator.getValue() == ">=" || UnaryOperator.getValue() == "<");
}

static bool isValidVersion(const version::Version &Version,
                           const version::Version &ExpectedVersion,
                           StringRef UnaryOperator) {
   if (UnaryOperator == ">=")
      return Version >= ExpectedVersion;
   if (UnaryOperator == "<")
      return Version < ExpectedVersion;
   llvm_unreachable("unsupported unary operator");
}

/// The condition validator.
class ValidateIfConfigCondition :
   public ExprVisitor<ValidateIfConfigCondition, Expr *> {
   AstContext &Ctx;
   DiagnosticEngine &D;

   bool HasError;

   /// Get the identifier string of the UnresolvedDeclRefExpr.
   Optional<StringRef> getDeclRefStr(Expr *E, DeclRefKind Kind) {
      auto UDRE = dyn_cast<UnresolvedDeclRefExpr>(E);
      if (!UDRE ||
          !UDRE->hasName() ||
          UDRE->getRefKind() != Kind ||
          UDRE->getName().isCompoundName())
         return None;

      return UDRE->getName().getBaseIdentifier().str();
   }

   Expr *diagnoseUnsupportedExpr(Expr *E) {
      D.diagnose(E->getLoc(),
                 diag::unsupported_conditional_compilation_expression_type);
      return nullptr;
   }

   // Support '||' and '&&' operator. The procedence of '&&' is higher than '||'.
   // Invalid operator and the next operand are diagnosed and removed from AST.
   Expr *foldSequence(Expr *LHS, ArrayRef<Expr *> &S, bool isRecurse = false) {
      assert(!S.empty() && ((S.size() & 1) == 0));

      auto getNextOperator = [&]() -> Optional<StringRef> {
         assert((S.size() & 1) == 0);
         while (!S.empty()) {
            auto Name = getDeclRefStr(S[0], DeclRefKind::BinaryOperator);
            if (Name.hasValue() && (*Name == "||" || *Name == "&&"))
               return Name;

            auto DiagID = isa<UnresolvedDeclRefExpr>(S[0])
                          ? diag::unsupported_conditional_compilation_binary_expression
                          : diag::unsupported_conditional_compilation_expression_type;
            D.diagnose(S[0]->getLoc(), DiagID);
            HasError |= true;
            // Consume invalid operator and the immediate RHS.
            S = S.slice(2);
         }
         return None;
      };

      // Extract out the first operator name.
      auto OpName = getNextOperator();
      if (!OpName.hasValue())
         // If failed, it's not a sequence anymore.
         return LHS;
      Expr *Op = S[0];

      // We will definitely be consuming at least one operator.
      // Pull out the prospective RHS and slice off the first two elements.
      Expr *RHS = validate(S[1]);
      S = S.slice(2);

      while (true) {
         // Pull out the next binary operator.
         auto NextOpName = getNextOperator();
         bool IsEnd = !NextOpName.hasValue();
         if (!IsEnd && *OpName == "||" && *NextOpName == "&&") {
            RHS = foldSequence(RHS, S, /*isRecurse*/true);
            continue;
         }

         // Apply the operator with left-associativity by folding the first two
         // operands.
         TupleExpr *Arg = TupleExpr::create(Ctx, SourceLoc(), {LHS, RHS},
                                            {}, {}, SourceLoc(),
            /*HasTrailingClosure=*/false,
            /*Implicit=*/true);
         LHS = new(Ctx) BinaryExpr(Op, Arg, /*implicit*/false);

         // If we don't have the next operator, we're done.
         if (IsEnd)
            break;
         if (isRecurse && *OpName == "&&" && *NextOpName == "||")
            break;

         OpName = NextOpName;
         Op = S[0];
         RHS = validate(S[1]);
         S = S.slice(2);
      }

      return LHS;
   }

public:
   ValidateIfConfigCondition(AstContext &Ctx, DiagnosticEngine &D)
      : Ctx(Ctx), D(D), HasError(false) {}

   // Explicit configuration flag.
   Expr *visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *E) {
      if (!getDeclRefStr(E, DeclRefKind::Ordinary).hasValue())
         return diagnoseUnsupportedExpr(E);
      return E;
   }

   // 'true' or 'false' constant.
   Expr *visitBooleanLiteralExpr(BooleanLiteralExpr *E) {
      return E;
   }

   // '0' and '1' are warned, but we accept it.
   Expr *visitIntegerLiteralExpr(IntegerLiteralExpr *E) {
      if (E->isNegative() ||
          (E->getDigitsText() != "0" && E->getDigitsText() != "1")) {
         return diagnoseUnsupportedExpr(E);
      }
      // "#if 0" isn't valid, but it is common, so recognize it and handle it
      // with a fixit.
      StringRef replacement = E->getDigitsText() == "0" ? "false" : "true";
      D.diagnose(E->getLoc(), diag::unsupported_conditional_compilation_integer,
                 E->getDigitsText(), replacement)
         .fixItReplace(E->getLoc(), replacement);
      return E;
   }

   // Platform conditions.
   Expr *visitCallExpr(CallExpr *E) {
      auto KindName = getDeclRefStr(E->getFn(), DeclRefKind::Ordinary);
      if (!KindName.hasValue()) {
         D.diagnose(E->getLoc(), diag::unsupported_platform_condition_expression);
         return nullptr;
      }

      auto *ArgP = dyn_cast<ParenExpr>(E->getArg());
      if (!ArgP) {
         D.diagnose(E->getLoc(), diag::platform_condition_expected_one_argument);
         return nullptr;
      }
      Expr *Arg = ArgP->getSubExpr();

      // '_compiler_version' '(' string-literal ')'
      if (*KindName == "_compiler_version") {
         auto SLE = dyn_cast<StringLiteralExpr>(Arg);
         if (!SLE) {
            D.diagnose(Arg->getLoc(),
                       diag::unsupported_platform_condition_argument,
                       "string literal");
            return nullptr;
         }

         auto ValStr = SLE->getValue();
         if (ValStr.empty()) {
            D.diagnose(SLE->getLoc(), diag::empty_version_string);
            return nullptr;
         }

         auto Val = version::Version::parseCompilerVersionString(
            SLE->getValue(), SLE->getLoc(), &D);
         if (!Val.has_value())
            return nullptr;
         return E;
      }

      // 'swift' '(' ('>=' | '<') float-literal ( '.' integer-literal )* ')'
      // 'compiler' '(' ('>=' | '<') float-literal ( '.' integer-literal )* ')'
      if (*KindName == "swift" || *KindName == "compiler") {
         auto PUE = dyn_cast<PrefixUnaryExpr>(Arg);
         Optional<StringRef> PrefixName =
            PUE ? getDeclRefStr(PUE->getFn(), DeclRefKind::PrefixOperator) : None;
         if (!isValidPrefixUnaryOperator(PrefixName)) {
            D.diagnose(
               Arg->getLoc(), diag::unsupported_platform_condition_argument,
               "a unary comparison '>=' or '<'; for example, '>=2.2' or '<2.2'");
            return nullptr;
         }
         auto versionString = extractExprSource(Ctx.SourceMgr, PUE->getArg());
         auto Val = version::Version::parseVersionString(
            versionString, PUE->getArg()->getStartLoc(), &D);
         if (!Val.has_value())
            return nullptr;
         return E;
      }

      // ( 'os' | 'arch' | '_endian' | '_runtime' | 'canImport') '(' identifier ')''
      auto Kind = getPlatformConditionKind(*KindName);
      if (!Kind.hasValue()) {
         D.diagnose(E->getLoc(), diag::unsupported_platform_condition_expression);
         return nullptr;
      }

      auto ArgStr = getDeclRefStr(Arg, DeclRefKind::Ordinary);
      if (!ArgStr.hasValue()) {
         D.diagnose(E->getLoc(), diag::unsupported_platform_condition_argument,
                    "identifier");
         return nullptr;
      }

      PlatformConditionKind suggestedKind = *Kind;
      std::vector<StringRef> suggestedValues;
      if (!LangOptions::checkPlatformConditionSupported(*Kind, *ArgStr,
                                                        suggestedKind, suggestedValues)) {
         if (Kind == PlatformConditionKind::Runtime) {
            // Error for _runtime()
            D.diagnose(Arg->getLoc(),
                       diag::unsupported_platform_runtime_condition_argument);
            return nullptr;
         }

         // Just a warning for other unsupported arguments.
         StringRef DiagName;
         switch (*Kind) {
            case PlatformConditionKind::OS:
               DiagName = "operating system";
               break;
            case PlatformConditionKind::Arch:
               DiagName = "architecture";
               break;
            case PlatformConditionKind::Endianness:
               DiagName = "endianness";
               break;
            case PlatformConditionKind::CanImport:
               DiagName = "import conditional";
               break;
            case PlatformConditionKind::TargetEnvironment:
               DiagName = "target environment";
               break;
            case PlatformConditionKind::Runtime:
               llvm_unreachable("handled above");
         }
         auto Loc = Arg->getLoc();
         D.diagnose(Loc, diag::unknown_platform_condition_argument,
                    DiagName, *KindName);
         if (suggestedKind != *Kind) {
            auto suggestedKindName = getPlatformConditionName(suggestedKind);
            D.diagnose(Loc, diag::note_typo_candidate, suggestedKindName)
               .fixItReplace(E->getFn()->getSourceRange(), suggestedKindName);
         }
         for (auto suggestion : suggestedValues)
            D.diagnose(Loc, diag::note_typo_candidate, suggestion)
               .fixItReplace(Arg->getSourceRange(), suggestion);
      }

      return E;
   }

   // Grouped condition. e.g. '(FLAG)'
   Expr *visitParenExpr(ParenExpr *E) {
      E->setSubExpr(validate(E->getSubExpr()));
      return E;
   }

   // Prefix '!'. Other prefix operators are rejected.
   Expr *visitPrefixUnaryExpr(PrefixUnaryExpr *E) {
      auto OpName = getDeclRefStr(E->getFn(), DeclRefKind::PrefixOperator);
      if (!OpName.hasValue() || *OpName != "!") {
         D.diagnose(E->getLoc(),
                    diag::unsupported_conditional_compilation_unary_expression);
         return nullptr;
      }
      E->setArg(validate(E->getArg()));
      return E;
   }

   // Fold sequence expression for non-Swift3 mode.
   Expr *visitSequenceExpr(SequenceExpr *E) {
      ArrayRef<Expr *> Elts = E->getElements();
      Expr *foldedExpr = validate(Elts[0]);
      Elts = Elts.slice(1);
      foldedExpr = foldSequence(foldedExpr, Elts);
      assert(Elts.empty());
      return foldedExpr;
   }

   // Other expression types are unsupported.
   Expr *visitExpr(Expr *E) {
      return diagnoseUnsupportedExpr(E);
   }

   Expr *validate(Expr *E) {
      if (auto E2 = visit(E))
         return E2;
      HasError |= true;
      return E;
   }

   bool hasError() const {
      return HasError;
   }
};

/// Validate and modify the condition expression.
/// Returns \c true if the condition contains any error.
static bool validateIfConfigCondition(Expr *&condition,
                                      AstContext &Context,
                                      DiagnosticEngine &D) {
   ValidateIfConfigCondition Validator(Context, D);
   condition = Validator.validate(condition);
   return Validator.hasError();
}

/// The condition evaluator.
/// The condition must be validated with validateIfConfigCondition().
class EvaluateIfConfigCondition :
   public ExprVisitor<EvaluateIfConfigCondition, bool> {
   AstContext &Ctx;

   /// Get the identifier string from an \c Expr assuming it's an
   /// \c UnresolvedDeclRefExpr.
   StringRef getDeclRefStr(Expr *E) {
      return cast<UnresolvedDeclRefExpr>(E)->getName().getBaseIdentifier().str();
   }

public:
   EvaluateIfConfigCondition(AstContext &Ctx) : Ctx(Ctx) {}

   bool visitBooleanLiteralExpr(BooleanLiteralExpr *E) {
      return E->getValue();
   }

   bool visitIntegerLiteralExpr(IntegerLiteralExpr *E) {
      return E->getDigitsText() != "0";
   }

   bool visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *E) {
      auto Name = getDeclRefStr(E);
      return Ctx.LangOpts.isCustomConditionalCompilationFlagSet(Name);
   }

   bool visitCallExpr(CallExpr *E) {
      auto KindName = getDeclRefStr(E->getFn());
      auto *Arg = cast<ParenExpr>(E->getArg())->getSubExpr();

      if (KindName == "_compiler_version") {
         auto Str = cast<StringLiteralExpr>(Arg)->getValue();
         auto Val = version::Version::parseCompilerVersionString(
            Str, SourceLoc(), nullptr).value();
         auto thisVersion = version::Version::getCurrentCompilerVersion();
         return thisVersion >= Val;
      } else if ((KindName == "swift") || (KindName == "compiler")) {
         auto PUE = cast<PrefixUnaryExpr>(Arg);
         auto PrefixName = getDeclRefStr(PUE->getFn());
         auto Str = extractExprSource(Ctx.SourceMgr, PUE->getArg());
         auto Val = version::Version::parseVersionString(
            Str, SourceLoc(), nullptr).value();
         if (KindName == "swift") {
            return isValidVersion(Ctx.LangOpts.EffectiveLanguageVersion, Val,
                                  PrefixName);
         } else if (KindName == "compiler") {
            auto currentLanguageVersion =
               version::Version::getCurrentLanguageVersion();
            return isValidVersion(currentLanguageVersion, Val, PrefixName);
         } else {
            llvm_unreachable("unsupported version conditional");
         }
      } else if (KindName == "canImport") {
         auto Str = extractExprSource(Ctx.SourceMgr, Arg);
         return Ctx.canImportModule({Ctx.getIdentifier(Str), E->getLoc()});
      }

      auto Val = getDeclRefStr(Arg);
      auto Kind = getPlatformConditionKind(KindName).getValue();
      return Ctx.LangOpts.checkPlatformCondition(Kind, Val);
   }

   bool visitPrefixUnaryExpr(PrefixUnaryExpr *E) {
      return !visit(E->getArg());
   }

   bool visitParenExpr(ParenExpr *E) {
      return visit(E->getSubExpr());
   }

   bool visitBinaryExpr(BinaryExpr *E) {
      auto OpName = getDeclRefStr(E->getFn());
      auto Args = E->getArg()->getElements();
      if (OpName == "||") return visit(Args[0]) || visit(Args[1]);
      if (OpName == "&&") return visit(Args[0]) && visit(Args[1]);
      llvm_unreachable("unsupported binary operator");
   }

   bool visitExpr(Expr *E) { llvm_unreachable("Unvalidated condition?"); }
};

/// Evaluate the condition.
/// \c true if success, \c false if failed.
static bool evaluateIfConfigCondition(Expr *Condition, AstContext &Context) {
   return EvaluateIfConfigCondition(Context).visit(Condition);
}

/// Version condition checker.
class IsVersionIfConfigCondition :
   public ExprVisitor<IsVersionIfConfigCondition, bool> {

   /// Get the identifier string from an \c Expr assuming it's an
   /// \c UnresolvedDeclRefExpr.
   StringRef getDeclRefStr(Expr *E) {
      return cast<UnresolvedDeclRefExpr>(E)->getName().getBaseIdentifier().str();
   }

public:
   IsVersionIfConfigCondition() {}

   bool visitBinaryExpr(BinaryExpr *E) {
      auto OpName = getDeclRefStr(E->getFn());
      auto Args = E->getArg()->getElements();
      if (OpName == "||") return visit(Args[0]) && visit(Args[1]);
      if (OpName == "&&") return visit(Args[0]) || visit(Args[1]);
      llvm_unreachable("unsupported binary operator");
   }

   bool visitCallExpr(CallExpr *E) {
      auto KindName = getDeclRefStr(E->getFn());
      return KindName == "_compiler_version" || KindName == "swift" ||
             KindName == "compiler";
   }

   bool visitPrefixUnaryExpr(PrefixUnaryExpr *E) { return visit(E->getArg()); }

   bool visitParenExpr(ParenExpr *E) { return visit(E->getSubExpr()); }

   bool visitExpr(Expr *E) { return false; }
};

/// Returns \c true if the condition is a version check.
static bool isVersionIfConfigCondition(Expr *Condition) {
   return IsVersionIfConfigCondition().visit(Condition);
}

/// Get the identifier string from an \c Expr if it's an
/// \c UnresolvedDeclRefExpr, otherwise the empty string.
static StringRef getDeclRefStr(Expr *E) {
   if (auto *UDRE = dyn_cast<UnresolvedDeclRefExpr>(E)) {
      return UDRE->getName().getBaseIdentifier().str();
   }
   return "";
}

static bool isPlatformConditionDisjunction(Expr *E, PlatformConditionKind Kind,
                                           ArrayRef<StringRef> Vals) {
   if (auto *Or = dyn_cast<BinaryExpr>(E)) {
      if (getDeclRefStr(Or->getFn()) == "||") {
         auto Args = Or->getArg()->getElements();
         return (isPlatformConditionDisjunction(Args[0], Kind, Vals) &&
                 isPlatformConditionDisjunction(Args[1], Kind, Vals));
      }
   } else if (auto *P = dyn_cast<ParenExpr>(E)) {
      return isPlatformConditionDisjunction(P->getSubExpr(), Kind, Vals);
   } else if (auto *C = dyn_cast<CallExpr>(E)) {
      if (getPlatformConditionKind(getDeclRefStr(C->getFn())) != Kind)
         return false;
      if (auto *ArgP = dyn_cast<ParenExpr>(C->getArg())) {
         if (auto *Arg = ArgP->getSubExpr()) {
            auto ArgStr = getDeclRefStr(Arg);
            for (auto V : Vals) {
               if (ArgStr == V)
                  return true;
            }
         }
      }
   }
   return false;
}

// Search for the first occurrence of a _likely_ (but not definite) implicit
// simulator-environment platform condition, or negation thereof. This is
// defined as any logical conjunction of one or more os() platform conditions
// _strictly_ from the set {iOS, tvOS, watchOS} and one or more arch() platform
// conditions _strictly_ from the set {i386, x86_64}.
//
// These are (at the time of writing) defined as de-facto simulators in
// Platform.cpp, and if a user is testing them they're _likely_ looking for
// simulator-ness indirectly. If there is anything else in the condition aside
// from these conditions (or the negation of such a conjunction), we
// conservatively assume the user is testing something other than
// simulator-ness.
static Expr *findAnyLikelySimulatorEnvironmentTest(Expr *Condition) {

   if (!Condition)
      return nullptr;

   if (auto *N = dyn_cast<PrefixUnaryExpr>(Condition)) {
      return findAnyLikelySimulatorEnvironmentTest(N->getArg());
   } else if (auto *P = dyn_cast<ParenExpr>(Condition)) {
      return findAnyLikelySimulatorEnvironmentTest(P->getSubExpr());
   }

   // We assume the user is writing the condition in CNF -- say (os(iOS) ||
   // os(tvOS)) && (arch(i386) || arch(x86_64)) -- rather than DNF, as the former
   // is exponentially more terse, and these conditions are already quite
   // unwieldy. If field evidence shows people using other variants, possibly add
   // them here.

   auto isSimulatorPlatformOSTest = [](Expr *E) -> bool {
      return isPlatformConditionDisjunction(
         E, PlatformConditionKind::OS, {"iOS", "tvOS", "watchOS"});
   };

   auto isSimulatorPlatformArchTest = [](Expr *E) -> bool {
      return isPlatformConditionDisjunction(
         E, PlatformConditionKind::Arch, {"i386", "x86_64"});
   };

   if (auto *And = dyn_cast<BinaryExpr>(Condition)) {
      if (getDeclRefStr(And->getFn()) == "&&") {
         auto Args = And->getArg()->getElements();
         if ((isSimulatorPlatformOSTest(Args[0]) &&
              isSimulatorPlatformArchTest(Args[1])) ||
             (isSimulatorPlatformOSTest(Args[1]) &&
              isSimulatorPlatformArchTest(Args[0]))) {
            return And;
         }
      }
   }
   return nullptr;
}

} // end anonymous namespace


/// Parse and populate a #if ... #endif directive.
/// Delegate callback function to parse elements in the blocks.
ParserResult<IfConfigDecl> Parser::parseIfConfig(
   llvm::function_ref<void(SmallVectorImpl<AstNode> &, bool)> parseElements) {
   // @todo
//   SyntaxParsingContext IfConfigCtx(SyntaxContext, SyntaxKind::IfConfigDecl);

   SmallVector<IfConfigClause, 4> Clauses;
   Parser::StructureMarkerRAII ParsingDecl(
      *this, Tok.getLoc(), Parser::StructureMarkerKind::IfConfig);

   bool shouldEvaluate =
      // Don't evaluate if it's in '-parse' mode, etc.
      State->PerformConditionEvaluation &&
      // If it's in inactive #if ... #endif block, there's no point to do it.
      !getScopeInfo().isInactiveConfigBlock();

   bool foundActive = false;
   bool isVersionCondition = false;
   while (1) {
      // @todo
//      SyntaxParsingContext ClauseContext(SyntaxContext,
//                                         SyntaxKind::IfConfigClause);

      bool isElse = Tok.is(tok::pound_else);
      SourceLoc ClauseLoc = consumeToken();
      Expr *Condition = nullptr;
      bool isActive = false;

      if (!Tok.isAtStartOfLine() && isElse && Tok.is(tok::kw_if)) {
         diagnose(Tok, diag::unexpected_if_following_else_compilation_directive)
            .fixItReplace(SourceRange(ClauseLoc, consumeToken()), "#elseif");
         isElse = false;
      }

      // Parse the condition.  Evaluate it to determine the active
      // clause unless we're doing a parse-only pass.
      if (isElse) {
         isActive = !foundActive && shouldEvaluate;
      } else {
         llvm::SaveAndRestore<bool> S(InPoundIfEnvironment, true);
         ParserResult<Expr> Result = parseExprSequence(diag::expected_expr,
            /*isBasic*/true,
            /*isForDirective*/true);
         if (Result.hasCodeCompletion())
            return makeParserCodeCompletionStatus();
         if (Result.isNull())
            return makeParserError();
         Condition = Result.get();
         if (validateIfConfigCondition(Condition, Context, Diags)) {
            // Error in the condition;
            isActive = false;
            isVersionCondition = false;
         } else if (!foundActive && shouldEvaluate) {
            // Evaluate the condition only if we haven't found any active one and
            // we're not in parse-only mode.
            isActive = evaluateIfConfigCondition(Condition, Context);
            isVersionCondition = isVersionIfConfigCondition(Condition);
         }
      }

      foundActive |= isActive;

      if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) {
         diagnose(Tok.getLoc(),
                  diag::extra_tokens_conditional_compilation_directive);
      }

      if (Expr *Test = findAnyLikelySimulatorEnvironmentTest(Condition)) {
         diagnose(Test->getLoc(),
                  diag::likely_simulator_platform_condition)
            .fixItReplace(Test->getSourceRange(),
                          "targetEnvironment(simulator)");
      }

      // Parse elements
      SmallVector<AstNode, 16> Elements;
      llvm::SaveAndRestore<bool> S(InInactiveClauseEnvironment,
                                   InInactiveClauseEnvironment || !isActive);
      if (isActive || !isVersionCondition) {
         parseElements(Elements, isActive);
      } /*else if (SyntaxContext->isEnabled()) {
         // We shouldn't skip code if we are building syntax tree.
         // The parser will keep running and we just discard the AST part.
         DiagnosticSuppression suppression(Context.Diags);
         SmallVector<AstNode, 16> dropedElements;
         parseElements(dropedElements, false);
      } */else {
         DiagnosticTransaction DT(Diags);
         skipUntilConditionalBlockClose();
         DT.abort();
      }

      Clauses.emplace_back(ClauseLoc, Condition,
                           Context.AllocateCopy(Elements), isActive);

      if (Tok.isNot(tok::pound_elseif, tok::pound_else))
         break;

      if (isElse)
         diagnose(Tok, diag::expected_close_after_else_directive);
   }
   // @todo
//   SyntaxContext->collectNodesInPlace(SyntaxKind::IfConfigClauseList);

   SourceLoc EndLoc;
   bool HadMissingEnd = parseEndIfDirective(EndLoc);

   auto *ICD = new(Context) IfConfigDecl(CurDeclContext,
                                         Context.AllocateCopy(Clauses),
                                         EndLoc, HadMissingEnd);
   return makeParserResult(ICD);
}

} // polar::llparser